package nz.ac.massey.cs.plot;

import static java.lang.Math.max;
import static nz.ac.massey.cs.stats.utils.Numeric.maximum;
import static nz.ac.massey.cs.stats.utils.Numeric.minimum;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

/**
 * A simple histogram buffered image generated from data vector
 *
 * @author Hussain Al-Mutawa
 * @version 1.0
 */
public class Histogram extends AbstractPlot {

	private Color barColor = new Color(204, 204, 255);
	private Color barBorderColor = Color.BLACK;
	private List<Double> data;

	/**
	 * generates a new Histogram image
	 */
	public Histogram() {
		this(new ArrayList<Double>());
	}

	public Histogram(List<? extends Number> data) {
		this(data, "Histogram of ...");
	}

	public Histogram(List<? extends Number> data, String title) {
		super();
		this.data = toDoubleList(data);
		setYmin(0);
		setYmax(0);
		setyLabel("Frequency");
		setTitle(title);
		setxClasses(15);
	}

	public void add(double point) {
		this.data.add(point);
	}

	@Override
	public void redraw(Graphics graphics) {
		Graphics2D g = (Graphics2D) getGraphics();
		double cw = getxClassWidth();

		setXmax(maximum(data).doubleValue() + (cw / 4));
		setXmin(minimum(data).doubleValue() - (cw / 4));

		computeNumberOfClasses();
		setxClasses(10);
		drawBackground(g);
		drawBars(g);
		drawXAxis(g);
		drawYAxis(g);
		drawYIntervals(g);
		drawXIntervals(g);
		drawTitle(g);
		drawXLabel(g);
		drawYLabel(g);
	}

	private void computeNumberOfClasses() {
		/*
		 * under 50, use five to seven classes,
		 * 50 to 99, use six to 10 classes,
		 * 100 to 249, use seven to 12 classes,
		 * 250 and over, use 10 to 20 classes.
		 */
		int sz = data.size();
		if (sz < 50) {
			setxClasses(5 + (sz / 10));
		} else if (sz < 100) {
			setxClasses(6 + (sz / 20));
		} else if (sz < 250) {
			setxClasses(7 + (sz / 40));
		} else {
			setxClasses(10 + (sz / 50));
		}
	}

	public void drawBars(Graphics2D g) {
		double[][] frequencies = getFrequencies();
		//System.out.println(Arrays.deepToString(frequencies));
		double xpos = getxAxisStart() - 5;
		double h = 0;

		for (int i = 0; i < frequencies.length; i++) {

			h = (getyAxisEnd() - getyAxisStart()) * frequencies[i][1];
			g.setColor(getBarBorderColor());
			g.draw(new Rectangle2D.Double(5 + xpos, gethOffset() - h - 5, getxClassWidth(), h));

			g.setColor(getBarColor());
			g.fill(new Rectangle2D.Double(8 + xpos, gethOffset() - h - 2, getxClassWidth() - 5, h - 5));

			xpos += getxClassWidth();
		}
	}

	private double[][] getFrequencies() {
		double groupRange = (getXmax() - getXmin()) / getxClasses();
		double step = getXmin();
		double fmax = Double.MIN_VALUE;
		double[][] frequencies = new double[getxClasses()][2];

		for (int i = 0; i < frequencies.length; i++) {
			frequencies[i][0] = step;
			step += groupRange;
		}

		for (double x : data) {
			setYmax(max(++frequencies[(int) ((x - getXmin() - 1) / groupRange)][1], getYmax()));
		}

		for (int f = 0; f < frequencies.length; f++) {
			frequencies[f][1] /= data.size();
			fmax = Math.max(fmax, frequencies[f][1]);
		}

		for (int f = 0; f < frequencies.length; f++) {
			frequencies[f][1] /= fmax;
		}

		return frequencies;
	}

	//    private int getClosesetGroup(double c, double[][] grps) {
	//        int p = 0;
	//        do{
	//        	if((p>0?c>grps[p-1][0]:true) && c<grps[p][0]) break;
	//        	p++;
	//        }while (1+p<grps.length);
	//        return p;
	//    }
	public Color getBarColor() {
		return barColor;
	}

	public Color getBarBorderColor() {
		return barBorderColor;
	}

	public static List<Double> toDoubleList(List<? extends Number> input) {
		List<Double> ret = new ArrayList<Double>();
		for (Number i : input) {
			ret.add(i.doubleValue());
		}
		return ret;
	}

	public String toTex(){
		StringBuffer sb = new StringBuffer();
		sb.append("\\documentclass{standalone}");
		sb.append("\n\\usepackage[T1]{fontenc}");
		sb.append("\n\\usepackage{tgtermes}");

		sb.append("\n\\usepackage{tikz}");
		sb.append("\n\\usepackage{pgfplots}");
		sb.append("\n\\usetikzlibrary{shapes,arrows,shadows}");
		sb.append("\n\\usetikzlibrary{shadows.blur}");
		sb.append("\n\\usetikzlibrary{shapes.symbols}");
		
		sb.append("\n\\begin{document}");
		sb.append("\n\\begin{tikzpicture}");
		
		//title
		sb.append("\n\\node at (0,0) {"+ getTitle() +"};");
		
		//g.draw(new Line2D.Double(getxAxisStart(), gethOffset(), getxAxisEnd(), gethOffset()));
		//x-axis
		sb.append("\n\\draw (), ()");
		
		sb.append("\n\\end{tikzpicture}");
		sb.append("\n\\end{document}");
		return sb.toString();
	}
}